package main

import (
	crand "crypto/rand"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/x509"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"math/big"
	"math/rand"
	"net"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const (
	privateKeyFile = "private.bytes.hex.txt"
	publicKeyFile  = "public.der.bytes.hex.txt"
	RSALen         = 512 // RSA 秘钥长度， >=384
)

// 详细参考: https://datatracker.ietf.org/doc/rfc3972
func main() {
	var (
		prefix           = "fe80:0000:0000:0000" // 网络地址
		sec       byte   = 1                     // 控制 CGA 破解复杂度，取值范围 [0, 7]。 数值越大越复杂，生成 CGA 耗费的时间越长
		extension []byte                         // 可选扩展字段
	)

	cgaAddr, modifierStr, pubHexStr, collisionNum, err := generateCGA(prefix, sec, extension)
	if err != nil {
		panic(err)
	}
	fmt.Println("CGA 地址 ===>> ", cgaAddr)

	if err = verifyCGA(cgaAddr, prefix, modifierStr, pubHexStr, collisionNum, extension); err != nil {
		panic(err)
	}
	fmt.Println("验证通过.")
}

// verifyCGA 验证 CGA
func verifyCGA(cgaAddr, prefix, modifierHexStr, pubHexStr string, collisionNum int, extension []byte) (err error) {
	fmt.Println("开始验证...")

	prefix = strings.ReplaceAll(prefix, ":", "")
	prefix = strings.ReplaceAll(prefix, " ", "")

	// 1. Check that the collision count in the CGA Parameters data
	//      structure is 0, 1, or 2.  The CGA verification fails if the
	//      collision count is out of the valid range.
	if collisionNum < 0 || collisionNum > 2 {
		panic(fmt.Errorf("collision 的值 [%v] 超出取值范围 [0, 2]", collisionNum))
	}

	// 2. Check that the subnet prefix in the CGA Parameters data structure
	//      is equal to the subnet prefix (i.e., the leftmost 64 bits) of the
	//      address.  The CGA verification fails if the prefix values differ.
	if strings.ReplaceAll(cgaAddr, ":", "")[0:16] != prefix {
		panic(fmt.Errorf("网络段(前64位)地址 [%v] 和 CGA [%v] 不匹配！", prefix, cgaAddr))
	}

	//   3. Execute the SHA-1 algorithm on the CGA Parameters data structure.
	//      Take the 64 leftmost bits of the SHA-1 hash value.  The result is
	//      Hash1.
	//   4. Compare Hash1 with the interface identifier (i.e., the rightmost
	//      64 bits) of the address.  Differences in the three leftmost bits
	//      and in bits 6 and 7 (i.e., the "u" and "g" bits) are ignored.  If
	//      the 64-bit values differ (other than in the five ignored bits),
	//      the CGA verification fails.
	var (
		empty                = "0000 0000 0000 0000 00"
		extensionHexStr      = hex.EncodeToString(extension)
		sec             byte = 0
		_                    = sec

		originStr = strings.Builder{}

		collision = collisionNum // DAD 控制，取值范围[0, 2]

		sum  []byte
		hash = sha1.New()
		bts  []byte
	)

	originStr.Reset()
	if _, err = originStr.WriteString(modifierHexStr); err != nil {
		return
	}
	if _, err = originStr.WriteString(prefix); err != nil {
		return
	}
	if _, err = originStr.WriteString(fmt.Sprintf("0%d", collision)); err != nil {
		return
	}
	if _, err = originStr.WriteString(pubHexStr); err != nil {
		return
	}
	if _, err = originStr.WriteString(extensionHexStr); err != nil {
		return
	}

	bts, err = read16Strings(originStr.String())
	if err != nil {
		panic(err)
	}

	hash.Reset()
	_, err = hash.Write(bts)
	if err != nil {
		panic(err)
	}
	sum = hash.Sum(nil)
	hash1String := hex.EncodeToString(sum[:])

	hash1Str := hash1String[:16]
	bytes, err := read16Strings(hash1Str)
	if err != nil {
		panic(err)
	}

	bytes[0] = bytes[0] & 0b11111100
	hash1String = hex.EncodeToString(bytes)

	cgaStrs := strings.ReplaceAll(cgaAddr, ":", "")[16:]
	bytesCGA, err := read16Strings(cgaStrs)
	if err != nil {
		panic(err)
	}

	if cgaStrs[4:] != hash1Str[4:] || bytesCGA[0]<<3>>3 != bytes[0]<<3>>3 {
		fmt.Println("CGA 地址 HASH1 值不匹配！")
	}

	//   5. Read the security parameter Sec from the three leftmost bits of
	//      the 64-bit interface identifier of the address.  (Sec is an
	//      unsigned 3-bit integer.)
	//   6. Concatenate from left to right the modifier, 9 zero octets, the
	//      public key, and any extension fields that follow the public key in
	//      the CGA Parameters data structure.  Execute the SHA-1 algorithm on
	//      the concatenation.  Take the 112 leftmost bits of the SHA-1 hash
	//      value.  The result is Hash2.
	//   7. Compare the 16*Sec leftmost bits of Hash2 with zero.  If any one
	//      of them is not zero, the CGA verification fails.  Otherwise, the
	//      verification succeeds.  (If Sec=0, the CGA verification never
	//      fails at this step.)
	sec = bytesCGA[0] >> 5

	originStr.Reset()
	if _, err = originStr.WriteString(modifierHexStr); err != nil {
		return
	}
	if _, err = originStr.WriteString(empty); err != nil {
		return
	}
	if _, err = originStr.WriteString(pubHexStr); err != nil {
		return
	}
	if _, err = originStr.WriteString(extensionHexStr); err != nil {
		return
	}

	bts, err = read16Strings(originStr.String())
	if err != nil {
		panic(err)
	}

	hash.Reset()
	_, err = hash.Write(bts)
	if err != nil {
		return
	}
	sum = hash.Sum(nil)

	hash2String := hex.EncodeToString(sum[:])

	if hash2String[:4*sec] != "0000" {
		panic(fmt.Errorf("HASH2 check SEC[%v] fail", sec))
	}

	return nil
}

// generateCGA 生成 CGA 主机地址
func generateCGA(prefix string, sec byte, extension []byte) (cgaAddr, modifierHexStr, pubHexStr string, collisionNum int, err error) {
	start := time.Now().UnixNano()

	prefix = strings.ReplaceAll(prefix, ":", "")

	modifier := randomModifier()

	pubKey, err := generateRSAkey()
	if err != nil {
		return
	}
	pubHexStr = hex.EncodeToString(pubKey)

	var (
		dlt             = big.NewInt(1)
		empty           = "0000 0000 0000 0000 00"
		extensionHexStr = hex.EncodeToString(extension)

		originStr = strings.Builder{}
		num       *big.Int
		cnt       int64 = 0 // hash2 累计计算次数

		collision = 0 // DAD 控制，取值范围[0, 2]

		sum  []byte
		hash = sha1.New()
	)

hash2:
	originStr.Reset()
	if _, err = originStr.WriteString(hex.EncodeToString(modifier)); err != nil {
		return
	}
	if _, err = originStr.WriteString(empty); err != nil {
		return
	}
	if _, err = originStr.WriteString(pubHexStr); err != nil {
		return
	}
	if _, err = originStr.WriteString(extensionHexStr); err != nil {
		return
	}

	bts, err := read16Strings(originStr.String())

	hash.Reset()
	_, err = hash.Write(bts)
	if err != nil {
		return
	}
	sum = hash.Sum(nil)

	hash2String := hex.EncodeToString(sum[:])

	fmt.Printf(">> %v << hash2String: %v, modifier: %v \n", cnt, hash2String, hex.EncodeToString(modifier))

	if hash2String[:4*sec] != "0000" {
		num = new(big.Int).SetBytes(modifier)
		num.Add(num, dlt)
		modifier = num.Bytes()
		cnt += 1
		goto hash2
	}

	end := time.Now().UnixNano()
	fmt.Printf("time cost ====>> %d nano seconds \n", end-start)

	collisionNum = collision
	modifierHexStr = hex.EncodeToString(modifier)

	privateKeyHex, err := os.ReadFile(privateKeyFile)
	if err != nil {
		fmt.Println(err)
	}
	publicKeyHex, err := os.ReadFile(publicKeyFile)
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println("private key in hex code ===>> ", string(privateKeyHex))
	fmt.Println("public key in hex code ===>> ", string(publicKeyHex))
	fmt.Println("hash2 input string ==>> ")
	printBytes2Hex(bts)
	fmt.Println("hash2 value ==>> ", hash2String)
	fmt.Println("modifier ===>> ")

hash1:
	originStr.Reset()
	if _, err = originStr.WriteString(modifierHexStr); err != nil {
		return
	}
	if _, err = originStr.WriteString(prefix); err != nil {
		return
	}
	if _, err = originStr.WriteString(fmt.Sprintf("0%d", collision)); err != nil {
		return
	}
	if _, err = originStr.WriteString(pubHexStr); err != nil {
		return
	}
	if _, err = originStr.WriteString(extensionHexStr); err != nil {
		return
	}

	bts, err = read16Strings(originStr.String())
	if err != nil {
		panic(err)
	}

	hash.Reset()
	_, err = hash.Write(bts)
	if err != nil {
		panic(err)
	}
	sum = hash.Sum(nil)
	hash1String := hex.EncodeToString(sum[:])

	hash1Str := hash1String[:16]
	bytes, err := read16Strings(hash1Str)
	if err != nil {
		panic(err)
	}

	bytes[0] = ((sec << 5) | (bytes[0] << 3 >> 3)) & 0b11111100
	hash1String = hex.EncodeToString(bytes)

	if 1 == 2 { // 地址重复检查
		if collision == 2 { // 超过 3 次直接退出
			panic("Duplicate interface address more than three times!")
		}

		collision += 1
		goto hash1
	}

	fmt.Println("hash1 input string ==>> ")
	printBytes2Hex(bts)

	fmt.Println("sec value ===>> ", sec)
	fmt.Println("collision value ===>> ", collision)
	fmt.Println("hash1 ===>> ", hash1String)

	splits, err := split4Hex(prefix + hash1String)
	if err != nil {
		fmt.Println(err)
	}

	cgaAddr = strings.Join(splits, ":")
	ip := net.ParseIP(cgaAddr)
	fmt.Println("CGA full addr is ===>> ", cgaAddr)
	fmt.Println("CGA addr is ===>> ", ip.String())

	return
}

func randomModifier() []byte {
	res := make([]byte, net.IPv6len)
	// 从密码级别的随机数生成器中填充数组
	for i := 0; i < net.IPv6len; i++ {
		res[i] = byte(rand.Intn(256))
	}
	return res
}

func printBytes2Hex(bts []byte) {
	for i := 0; i < len(bts); i++ {
		if (i)%24 == 0 {
			fmt.Println()
		}
		if (i)%2 == 0 {
			fmt.Printf(" ")
		}
		fmt.Printf("%02x", bts[i])
	}
	fmt.Println()
}

func read16Strings(strs string) ([]byte, error) {
	split, err := split4Hex(strs)
	if err != nil {
		return nil, err
	}

	bts := []byte{}
	for i := range split {
		parseUint, err := strconv.ParseUint("0x"+split[i], 0, 16)
		if err != nil {
			return nil, err
		}
		bts = binary.BigEndian.AppendUint16(bts, uint16(parseUint))
	}
	return bts, nil
}

func split4Hex(strs string) ([]string, error) {
	compile, err := regexp.Compile("\\s+")
	if err != nil {
		return nil, err
	}
	strs = compile.ReplaceAllString(strs, "")
	tmp := ""
	for i := 0; i < len(strs)%4; i++ {
		tmp += "0"
	}
	strs += tmp

	sp, err := regexp.Compile("\\w{1,4}")
	if err != nil {
		return nil, err
	}
	split := sp.FindAllString(strs, -1)
	return split, nil
}

func generateRSAkey() ([]byte, error) {
	// 生成 RSALen 位的 RSA 密钥对
	privateKey, err := rsa.GenerateKey(crand.Reader, RSALen)
	if err != nil {
		fmt.Println("Failed to generate RSA private key:", err)
		return nil, err
	}

	// 获取公钥
	publicKey := &privateKey.PublicKey

	// 将公钥编码为 DER 格式
	publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
	if err != nil {
		fmt.Println("Failed to encode public key:", err)
		return nil, err
	}

	// 将 DER 编码的公钥写入文件
	saveHex(publicKeyBytes, publicKeyFile)
	saveHex(privateKey.D.Bytes(), privateKeyFile)

	return publicKeyBytes, nil
}

func saveHex(bytes []byte, path string) {
	sb := strings.Builder{}
	for i := 0; i < len(bytes); i++ {
		if (i)%24 == 0 {
			sb.WriteString("\n")
		}
		if (i)%2 == 0 {
			sb.WriteString(" ")
		}

		sb.WriteString(fmt.Sprintf("%02x", bytes[i]))
	}
	fmt.Println(sb.String())

	publicKeyFile, err := os.Create(path)
	if err != nil {
		fmt.Println("Failed to create public key file:", err)
		return
	}
	defer publicKeyFile.Close()
	if _, err := publicKeyFile.WriteString(sb.String()); err != nil {
		fmt.Println("Failed to write public key to file:", err)
		return
	}
}
